{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "256d3948",
   "metadata": {},
   "source": [
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain-academy/blob/main/module-2/state-schema.ipynb) [![Open in LangChain Academy](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66e9eba12c7b7688aa3dbb5e_LCA-badge-green.svg)](https://academy.langchain.com/courses/take/intro-to-langgraph/lessons/58239426-lesson-1-state-schema)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f118fabe-37b7-4cd4-b7a4-9b0fc3875ca3",
   "metadata": {},
   "source": [
    "# State Schema \n",
    "\n",
    "## Review\n",
    "\n",
    "In module 1, we laid the foundations! We built up to an agent that can: \n",
    "\n",
    "* `act` - let the model call specific tools \n",
    "* `observe` - pass the tool output back to the model \n",
    "* `reason` - let the model reason about the tool output to decide what to do next (e.g., call another tool or just respond directly)\n",
    "* `persist state` - use an in memory checkpointer to support long-running conversations with interruptions\n",
    " \n",
    "And, we showed how to serve it locally in LangGraph Studio or deploy it with LangGraph Cloud. \n",
    "\n",
    "## Goals\n",
    "\n",
    "In this module, we're going to build a deeper understanding of both state and memory.\n",
    "\n",
    "First, let's review a few different ways to define your state schema."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b9a896f4-8509-456a-9a25-46532342f459",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f7927b0-9909-4e54-b997-ac49c1aeaa09",
   "metadata": {},
   "source": [
    "## Schema\n",
    "\n",
    "When we define a LangGraph `StateGraph`, we use a [state schema](https://langchain-ai.github.io/langgraph/concepts/low_level/#state).\n",
    "\n",
    "The state schema represents the structure and types of data that our graph will use.\n",
    "\n",
    "All nodes are expected to communicate with that schema.\n",
    "\n",
    "LangGraph offers flexibility in how you define your state schema, accommodating various Python [types](https://docs.python.org/3/library/stdtypes.html#type-objects) and validation approaches!\n",
    "\n",
    "## TypedDict\n",
    "\n",
    "As we mentioned in Module 1, we can use the `TypedDict` class from python's `typing` module.\n",
    "\n",
    "It allows you to specify keys and their corresponding value types.\n",
    " \n",
    "But, note that these are type hints. \n",
    "\n",
    "They can be used by static type checkers (like [mypy](https://github.com/python/mypy)) or IDEs to catch potential type-related errors before the code is run. \n",
    "\n",
    "But they are not enforced at runtime!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "eedb39f0-af0f-4794-bc16-65980d278b59",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing_extensions import TypedDict\n",
    "\n",
    "class TypedDictState(TypedDict):\n",
    "    foo: str\n",
    "    bar: str"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d5a71661-1086-455f-a5e0-a6d104034a95",
   "metadata": {},
   "source": [
    "For more specific value constraints, you can use things like the `Literal` type hint.\n",
    "\n",
    "Here, `mood` can only be either \"happy\" or \"sad\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4ad9749c-b127-433f-baa3-189a9349e9f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Literal\n",
    "\n",
    "class TypedDictState(TypedDict):\n",
    "    name: str\n",
    "    mood: Literal[\"happy\",\"sad\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1a9152d-1728-4a67-9e23-1ef622525047",
   "metadata": {},
   "source": [
    "We can use our defined state class (e.g., here `TypedDictState`) in LangGraph by simply passing it to `StateGraph`.\n",
    "\n",
    "And, we can think about each state key as just a \"channel\" in our graph. \n",
    "\n",
    "As discussed in Module 1, we overwrite the value of a specified key or \"channel\" in each node."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "2f7a0d6d-f70b-44ed-86e3-7cdb39873ba4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import random\n",
    "from IPython.display import Image, display\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "def node_1(state):\n",
    "    print(\"---Node 1---\")\n",
    "    return {\"name\": state['name'] + \" is ... \"}\n",
    "\n",
    "def node_2(state):\n",
    "    print(\"---Node 2---\")\n",
    "    return {\"mood\": \"happy\"}\n",
    "\n",
    "def node_3(state):\n",
    "    print(\"---Node 3---\")\n",
    "    return {\"mood\": \"sad\"}\n",
    "\n",
    "def decide_mood(state) -> Literal[\"node_2\", \"node_3\"]:\n",
    "        \n",
    "    # Here, let's just do a 50 / 50 split between nodes 2, 3\n",
    "    if random.random() < 0.5:\n",
    "\n",
    "        # 50% of the time, we return Node 2\n",
    "        return \"node_2\"\n",
    "    \n",
    "    # 50% of the time, we return Node 3\n",
    "    return \"node_3\"\n",
    "\n",
    "# Build graph\n",
    "builder = StateGraph(TypedDictState)\n",
    "builder.add_node(\"node_1\", node_1)\n",
    "builder.add_node(\"node_2\", node_2)\n",
    "builder.add_node(\"node_3\", node_3)\n",
    "\n",
    "# Logic\n",
    "builder.add_edge(START, \"node_1\")\n",
    "builder.add_conditional_edges(\"node_1\", decide_mood)\n",
    "builder.add_edge(\"node_2\", END)\n",
    "builder.add_edge(\"node_3\", END)\n",
    "\n",
    "# Add\n",
    "graph = builder.compile()\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "724bb640-2b0e-46c1-9416-b5bcdb9c17c8",
   "metadata": {},
   "source": [
    "Because our state is a dict, we simply invoke the graph with a dict to set an initial value of the `name` key in our state."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "74e09d32-6a08-4250-b19a-1f701828829d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---Node 1---\n",
      "---Node 2---\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'name': 'Lance is ... ', 'mood': 'happy'}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke({\"name\":\"Lance\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70cc5368-18b8-49c7-b561-41888b092311",
   "metadata": {},
   "source": [
    "## Dataclass\n",
    "\n",
    "Python's [dataclasses](https://docs.python.org/3/library/dataclasses.html) provide [another way to define structured data](https://www.datacamp.com/tutorial/python-data-classes).\n",
    "\n",
    "Dataclasses offer a concise syntax for creating classes that are primarily used to store data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "d576fc2c-350b-42ad-89e5-f93ae102dbf8",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dataclasses import dataclass\n",
    "\n",
    "@dataclass\n",
    "class DataclassState:\n",
    "    name: str\n",
    "    mood: Literal[\"happy\",\"sad\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64482b93-3c8f-4a30-925f-9be64e4b8b6f",
   "metadata": {},
   "source": [
    "To access the keys of a `dataclass`, we just need to modify the subscripting used in `node_1`: \n",
    "\n",
    "* We use `state.name` for the `dataclass` state rather than `state[\"name\"]` for the `TypedDict` above\n",
    "\n",
    "You'll notice something a bit odd: in each node, we still return a dictionary to perform the state updates.\n",
    " \n",
    "This is possible because LangGraph stores each key of your state object separately.\n",
    "\n",
    "The object returned by the node only needs to have keys (attributes) that match those in the state!\n",
    "\n",
    "In this case, the `dataclass` has key `name` so we can update it by passing a dict from our node, just as we did when state was a `TypedDict`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "1e1eda69-916f-4f6e-b400-6e65f73d8716",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def node_1(state):\n",
    "    print(\"---Node 1---\")\n",
    "    return {\"name\": state.name + \" is ... \"}\n",
    "\n",
    "# Build graph\n",
    "builder = StateGraph(DataclassState)\n",
    "builder.add_node(\"node_1\", node_1)\n",
    "builder.add_node(\"node_2\", node_2)\n",
    "builder.add_node(\"node_3\", node_3)\n",
    "\n",
    "# Logic\n",
    "builder.add_edge(START, \"node_1\")\n",
    "builder.add_conditional_edges(\"node_1\", decide_mood)\n",
    "builder.add_edge(\"node_2\", END)\n",
    "builder.add_edge(\"node_3\", END)\n",
    "\n",
    "# Add\n",
    "graph = builder.compile()\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06beb50a-4878-4d7e-ac6c-d60a0f417eb3",
   "metadata": {},
   "source": [
    "We invoke with a `dataclass` to set the initial values of each key / channel in our state!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "8c042325-e93d-43e1-9ac7-a0e20c2fb08d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---Node 1---\n",
      "---Node 3---\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'name': 'Lance is ... ', 'mood': 'sad'}"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke(DataclassState(name=\"Lance\",mood=\"sad\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2405e49b-e786-4bf9-ac85-1fb941d01bcd",
   "metadata": {},
   "source": [
    "## Pydantic\n",
    "\n",
    "As mentioned, `TypedDict` and `dataclasses` provide type hints but they don't enforce types at runtime. \n",
    " \n",
    "This means you could potentially assign invalid values without raising an error!\n",
    "\n",
    "For example, we can set `mood` to `mad` even though our type hint specifies `mood: list[Literal[\"happy\",\"sad\"]]`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "522fcc76-abf7-452a-9d7b-000e06942d94",
   "metadata": {},
   "outputs": [],
   "source": [
    "dataclass_instance = DataclassState(name=\"Lance\", mood=\"mad\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f095c3a-96b5-4318-9303-20424b4455e9",
   "metadata": {},
   "source": [
    "[Pydantic](https://docs.pydantic.dev/latest/api/base_model/) is a data validation and settings management library using Python type annotations. \n",
    "\n",
    "It's particularly well-suited [for defining state schemas in LangGraph](https://langchain-ai.github.io/langgraph/how-tos/state-model/) due to its validation capabilities.\n",
    "\n",
    "Pydantic can perform validation to check whether data conforms to the specified types and constraints at runtime."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "62e8720e-217f-4b98-837a-af45c3fa577f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Validation Error: 1 validation error for PydanticState\n",
      "mood\n",
      "  Input should be 'happy' or 'sad' [type=literal_error, input_value='mad', input_type=str]\n",
      "    For further information visit https://errors.pydantic.dev/2.8/v/literal_error\n"
     ]
    }
   ],
   "source": [
    "from pydantic import BaseModel, field_validator, ValidationError\n",
    "\n",
    "class PydanticState(BaseModel):\n",
    "    name: str\n",
    "    mood: str # \"happy\" or \"sad\" \n",
    "\n",
    "    @field_validator('mood')\n",
    "    @classmethod\n",
    "    def validate_mood(cls, value):\n",
    "        # Ensure the mood is either \"happy\" or \"sad\"\n",
    "        if value not in [\"happy\", \"sad\"]:\n",
    "            raise ValueError(\"Each mood must be either 'happy' or 'sad'\")\n",
    "        return value\n",
    "\n",
    "try:\n",
    "    state = PydanticState(name=\"John Doe\", mood=\"mad\")\n",
    "except ValidationError as e:\n",
    "    print(\"Validation Error:\", e)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f29913ca-0295-48eb-af4e-cae515dd9a9c",
   "metadata": {},
   "source": [
    "We can use `PydanticState` in our graph seamlessly. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "91db3393-b7f8-46e5-8129-0e7539b2804c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Build graph\n",
    "builder = StateGraph(PydanticState)\n",
    "builder.add_node(\"node_1\", node_1)\n",
    "builder.add_node(\"node_2\", node_2)\n",
    "builder.add_node(\"node_3\", node_3)\n",
    "\n",
    "# Logic\n",
    "builder.add_edge(START, \"node_1\")\n",
    "builder.add_conditional_edges(\"node_1\", decide_mood)\n",
    "builder.add_edge(\"node_2\", END)\n",
    "builder.add_edge(\"node_3\", END)\n",
    "\n",
    "# Add\n",
    "graph = builder.compile()\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e96c78be-b483-4fa4-949b-62d4274e97ac",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---Node 1---\n",
      "---Node 3---\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'name': 'Lance is ... ', 'mood': 'sad'}"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke(PydanticState(name=\"Lance\",mood=\"sad\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8119232a-7d56-4abc-b0ef-18bf5f0cc9fd",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
